package hdikow;

import gui.AlgorithmParameter;
import gui.BioinfAlgorithm;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Vector;
import java.util.regex.Pattern;

/**
 *
 * @author Heidi Dikow
 */
public class Needleman extends BioinfAlgorithm {

    private String name = "Needleman-Wunsch";
    private String description = "The Needleman-Wunsch Algorithm finds the best"
            + " alignment for two strings";
    protected String a ;
    protected String b ;
    protected int gapPenalty;
    //name of textfile containing evaluation Matrix 
    protected String matchMissmatch;
    protected MatrixEntry[][] matrix;
    protected int[][] scoringMatrix;
    protected HashMap<String, Integer> characterIndex;
    // tells if all possible alignments are caclulated
    protected boolean allAlignments;
    //String List for all possible alignments  
    protected ArrayList<String> alignments;

    /**
     * Constructs a Needleman-Wunsch Algorithm with default input values
     */
    public Needleman() {

        alignments = new ArrayList<>();

           super.parameters.add(new AlgorithmParameter(
                "First String", "This is the first String",
                String.class, "SDE"));

        super.parameters.add(new AlgorithmParameter(
                "Second String", "This is the second String",
                String.class, "SDESDE"));

        super.parameters.add(new AlgorithmParameter(
                "Match/Missmatch Evaluation",
                "This boolean tells if PAM250 is used as"
                + "Match/Missmatch evaluation or BLOSUM62",
                Boolean.class, new Boolean(true)));

        super.parameters.add(new AlgorithmParameter(
                "Gap-Penalty", "This is the value of the gap penalty for "
                + "inserting gaps", Integer.class, new Integer(-1)));

        super.parameters.add(new AlgorithmParameter(
                "Complete Traceback", "show all possible alignments",
                Boolean.class, new Boolean(true)));
    }

    @Override
    public String run(Vector<AlgorithmParameter> params) {
        //######### READ PARAMETERS ############################
       
        a = (String) params.elementAt(0).data;
        b = (String) params.elementAt(1).data;
        //checkbox if true means PAM250, false BLOSUM62
        boolean pam = (boolean) params.elementAt(2).data;
        matchMissmatch = (pam) ? "pam.txt" : "blosum.txt";
        gapPenalty = (int) params.elementAt(3).data;
        scoringMatrix = Utility.buildEvaluationMatrix(matchMissmatch);
        characterIndex = Utility.buildCharacterIndex(matchMissmatch);
        allAlignments = (boolean) params.elementAt(4).data;

        /*
         * check if the format of the input is correct if not do not execute
         * the program and return error message
         */
        if (!Utility.correctInput(a, b)) {
            return ("You inserted characters which are not in the single "
                    + "letter code format! \nIf you want to use this program "
                    + "please correct your input if not have a great day!");
        }
        /*
         *insert a random character in front of the sequences as the matrix 
         * needs to have the size of a.length()+1 and b.length()+1
         */
        a = "-" + a;
        b = "-" + b;
        //construct traceback matrix
        constructMatrix();
        alignments = completeTraceback(matrix, a.length() - 1,
                b.length() - 1, "", "", "");
        if (allAlignments) {
            String returnString = "";
            for (String s : alignments) {
                returnString += s + "\n \n";
            }
            return returnString;
        }
        return alignments.get(0);

    }

 

    /**
     *
     */
    public void constructMatrix() {

        matrix = new MatrixEntry[a.length()][b.length()];
        /*
         * Initialization
         */
        matrix[0][0] = new MatrixEntry(0);
        for (int i = 1; i < a.length(); i++) {
            matrix[i][0] = new MatrixEntry(matrix[i - 1][0].getEntry()
                    + gapPenalty);
        }
        for (int j = 1; j < b.length(); j++) {
            matrix[0][j] = new MatrixEntry(matrix[0][j - 1].getEntry()
                    + gapPenalty);
        }

        /*
         * Recursive construction
         */
        for (int i = 1; i < a.length(); i++) {
            for (int j = 1; j < b.length(); j++) {
                //score 
                int score;
                //create new object of type MatrixEntry
                MatrixEntry entry = new MatrixEntry();
                //see for which characters the score is calculated
                String c1 = a.substring(i, i + 1);
                String c2 = b.substring(j, j + 1);
                //index of characters to look up the score                
                int indexOfA = characterIndex.get(c1) - 1;
                int indexOfB = characterIndex.get(c2) - 1;
                //look up score
                score = scoringMatrix[indexOfA][indexOfB];
                /*
                 * calculate the three different source possiblilities 
                 * for dynamic construction of the matrix
                 */
                //check field left above
                int la = matrix[i - 1][j - 1].getEntry() + score;
                //check field left 
                int l = matrix[i - 1][j].getEntry() + gapPenalty;
                //check field above
                int ab = matrix[i][j - 1].getEntry() + gapPenalty;
                /*
                 * calculate which of the possible source has the maximal 
                 * value
                 */
                int max = Utility.maxValue(la,l,ab);
                /*
                 * check if the two characters are equal, used for
                 * the match/missmatch output line
                 */
                if (c1.equals(c2)) {
                    entry.setMatch(true);
                }
                /*
                 * check which of the three sources is a possible source field
                 */
                if (la == max) {
                    entry.setLeftAbove(true);
                }
                //left
                if (l == max) {
                    entry.setLeft(true);
                }
                //above
                if (ab == max) {
                    entry.setAbove(true);
                }

                entry.setEntry(max);
                matrix[i][j] = entry;
            }
        }
//        //print 
//        for (int i = 0; i < a.length(); i++) {
//            for (int j = 0; j < b.length(); j++) {
//                System.out.print(matrix[i][j].getEntry() + "#");
//            }
//            System.out.println();
//
//            System.out.println();
//        }
    }

    /**
     *
     * @param matrix
     * @param ia
     * @param ib
     * @param aliA
     * @param aliB
     * @param aliM
     * @return
     */
    public ArrayList<String> completeTraceback(MatrixEntry[][] matrix,
            int ia, int ib, String aliA, String aliB, String aliM) {
        
     

        ArrayList<String> giveBack = new ArrayList();
        if (ib == 0 || ia == 0) {
            while (ib != 0) {
                aliA = "-" + aliA;
                aliB = b.substring(ib, ib + 1) + aliB;
                aliM = " " + aliM;
                ib--;
            }
            while (ia != 0) {
                aliA = a.substring(ia, ia + 1) + aliA;
                aliB = "-" + aliB;
                aliM = " " + aliM;
                ia--;
            }
            giveBack.add("Seq1  " + aliA + "\n" + "      "
                    + aliM + "\n" + "Seq2  " + aliB);

        }

        if (matrix[ia][ib].isAbove()) {
            String t1 = "-" + aliA;
            String t2 = b.substring(ib, ib + 1) + aliB;
            String t3 = " " + aliM;
            ArrayList<String> innerList =
                    completeTraceback(matrix, ia, ib - 1, t1, t2, t3);
            for (String ali : innerList) {
                giveBack.add(ali);
            }

        }
        if (matrix[ia][ib].isLeft()) {
            String t1 = a.substring(ia, ia + 1) + aliA;
            String t2 = "-" + aliB;
            String t3 = " " + aliM;
            ArrayList<String> innerList =
                    completeTraceback(matrix, ia - 1, ib, t1, t2, t3);
            for (String ali : innerList) {
                giveBack.add(ali);
            }
        }
        if (matrix[ia][ib].isLeftAbove()) {
            String t1 = a.substring(ia, ia + 1) + aliA;
            String t2 = b.substring(ib, ib + 1) + aliB;
            String t3 = (matrix[ia][ib].isMatch()) ? "*" + aliM : ":" + aliM;
            ArrayList<String> innerList =
                    completeTraceback(matrix, ia - 1, ib - 1, t1, t2, t3);
            for (String ali : innerList) {
                giveBack.add(ali);
            }
        }

        return giveBack;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public String getDescription() {
        return this.description;
    }

    @Override
    public Vector<AlgorithmParameter> getInputParameters() {
        return super.parameters;
    }
}
